package top.wuchu.rpc.transport.netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import top.wuchu.rpc.codec.CommonDecoder;
import top.wuchu.rpc.codec.CommonEncoder;
import top.wuchu.rpc.hook.ShutdownHook;
import top.wuchu.rpc.provider.ServiceProviderImpl;
import top.wuchu.rpc.registry.NacosServiceRegistry;
import top.wuchu.rpc.serializer.CommonSerializer;
import top.wuchu.rpc.transport.AbstractRpcServer;

import java.util.concurrent.TimeUnit;

/**
 * NIO方式服务提供侧
 *
 * @author Wuchu
 */
public class NettyServer extends AbstractRpcServer {

    private final CommonSerializer serializer;

    public NettyServer(String host, int port) {
        this(host, port, DEFAULT_SERIALIZER);
    }

    public NettyServer(String host, int port, Integer serializer) {
        this.host = host;
        this.port = port;
        serviceRegistry = new NacosServiceRegistry();
        serviceProvider = new ServiceProviderImpl();
        this.serializer = CommonSerializer.getByCode(serializer);
        scanServices();
    }

    @Override
    public void start() {
        ShutdownHook.getShutdownHook().addClearAllHook();

        // bossGroup用来处理接收客户端连接的循环组
        EventLoopGroup bossGroup = new NioEventLoopGroup();

        // workerGroup用来处理客户端请求的循环组
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {

            /**
             * ServerBootstrap 是服务端的引导类，ServerBootstrap 在调用 bind() 方法时会创建一个 ServerChannel 来接受来自客户端的连接，
             * 并且该 ServerChannel 管理了多个子 Channel 用于同客户端之间的通信。
             */
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .option(ChannelOption.SO_BACKLOG, 256)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();

                            /* 这里我们设置30秒的读空闲时间，如果30秒都没收到任何客户端发来的请求，就关闭channel
                             * IdleStateHandler
                             *   readerIdleTime：指定读超时时间，指定 0 表明为禁用。
                             *	 writerIdleTime：指定写超时时间，指定 0 表明为禁用。
                             *	 allIdleTime：在指定读写超时时间，指定 0 表明为禁用。
                             */
                            pipeline.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS)) // 超时策略
                                    .addLast(new CommonEncoder(serializer)) // 添加自定义编码器
                                    .addLast(new CommonDecoder())  // 添加自定义解码器
                                    .addLast(new NettyServerHandler());  // 添加自定义handler
                        }
                    });
            ChannelFuture future = serverBootstrap.bind(host, port).sync();  // 后面加.sync()的作用是让服务端初始化完成之后才能进行下一步操作
            future.channel().closeFuture().sync();

        } catch (InterruptedException e) {
            logger.error("启动服务器时有错误发生: ", e);
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}
